package com.legendframework.core.dao;

import com.legendframework.core.dao.annotation.Entity;
import com.legendframework.core.dao.enums.DataType;
import com.legendframework.core.dao.exception.DaoException;
import com.legendframework.core.dao.sql.SqlDaoImpl;
import com.legendframework.core.dao.transaction.JdbcTransaction;
import com.legendframework.core.dao.transaction.YamlTransaction;
import com.legendframework.core.dao.utils.DaoStringUtils;
import com.legendframework.core.dao.yaml.DefaultManyToOneYamlDaoImpl;
import com.legendframework.core.dao.yaml.DefaultOneToManyYamlDaoImpl;
import com.legendframework.core.dao.yaml.DefaultOneToOneYamlDaoImpl;
import sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl;

import java.lang.reflect.*;
import java.util.function.Function;

/**
 * 实体类DAO生成器
 *
 * 1. 生成表
 * 2. 生成Dao是实现类
 *
 */
public class EntityDaoGenerate {

    private EntityDaoGenerate(){}

    /**
     * 根据实体类字节码对象生成Dao对象
     * @param entityType 实体类字节码对象
     * @param dataResources 数据底层所需数据
     * @param iocHandle IOC容器处理器，预期是需要外层IOC提供实现方法对Dao实现类进行动态代理
     * @param <T> 实体类类型
     * @return Dao对象
     */
    private static <T> IDao<T> generate(Class<T> entityType , DataResources dataResources, Function<Object,Object> iocHandle , JdbcTransaction jdbcTransaction , YamlTransaction yamlTransaction){
        if (!entityType.isAnnotationPresent(Entity.class)) {
            throw new DaoException(String.format("实体类: %s , 没有找到@Entity注解",entityType.getName()));
        }
        Entity entity = entityType.getAnnotation(Entity.class);
        //获取这个实体类最终的存储类型
        DataType type = entity.type() == DataType.UNDEFINED ? dataResources.getDataType() : entity.type();
        if (type == DataType.MYSQL) {
            if (dataResources.getDataSource() == null) {
                throw new DaoException("JDBC数据源为空");
            }
            return (IDao<T>) iocHandle.apply(new SqlDaoImpl<>(entityType,dataResources.getDataSource(),jdbcTransaction));
        }else if (type == DataType.YAML) {
            if (DaoStringUtils.isEmpty(entity.yamlFileName())){
                throw new DaoException(String.format("数据层类型: %s , 实体类: %s , 的@Entity$yamlFileName未填写",dataResources.getDataType(),entityType.getName()));
            }
            if (DaoStringUtils.isEmpty(dataResources.getYamlParentPath())) {
                throw new DaoException("yamlParentPath未空");
            }
            switch (entity.yamlStorageType()){
                case ONE_TO_ONE:
                    return (IDao<T>) iocHandle.apply(new DefaultOneToOneYamlDaoImpl<>(dataResources.getYamlParentPath(), entityType,yamlTransaction));
                case ONE_TO_MANY:
                    return (IDao<T>) iocHandle.apply(new DefaultOneToManyYamlDaoImpl<>(dataResources.getYamlParentPath(), entityType,yamlTransaction));
                case MANY_TO_ONE:
                    return (IDao<T>) iocHandle.apply(new DefaultManyToOneYamlDaoImpl<>(dataResources.getYamlParentPath(), entityType,yamlTransaction));
            }
        }
        throw new DaoException("请选择数据存储类型");
    }

    /**
     * 根据实体类Dao字节码对象生成Dao实例化对象
     * @param entityDaoType 实体类Dao接口字节码对象
     * @param dataResources 数据底层所需数据
     * @param iocHandle IOC容器处理器，预期是需要外层IOC提供实现方法对Dao实现类进行动态代理
     * @param <M> 实体类DAO类型
     * @return Dao实例化对象
     */
    public static <M extends IDao> IDao getDaoByDaoType(Class<M> entityDaoType , DataResources dataResources, Function<Object,Object> iocHandle , JdbcTransaction jdbcTransaction , YamlTransaction yamlTransaction){
        Class<?> entityClass = null;
        for (Type genericInterface : entityDaoType.getGenericInterfaces()) {
            if (IDao.class.isAssignableFrom(((ParameterizedTypeImpl) genericInterface).getRawType())){
                entityClass = (Class) ((ParameterizedType) genericInterface).getActualTypeArguments()[0];
            }
        }
        IDao dao = getDaoByEntityType(entityClass, dataResources,jdbcTransaction,yamlTransaction);
        return dao;
    }

    /**
     * 根据实体类Dao字节码对象生成Dao实例化对象
     * @param entityDaoType 实体类Dao接口字节码对象
     * @param dataResources 数据底层所需数据
     * @param <M> 实体类DAO类型
     * @return Dao实例化对象
     */
    public static <M extends IDao> IDao getDaoByDaoType(Class<M> entityDaoType , DataResources dataResources, JdbcTransaction jdbcTransaction , YamlTransaction yamlTransaction){
        return getDaoByDaoType(entityDaoType,dataResources,item -> item,jdbcTransaction,yamlTransaction);
    }

    /**
     * 根据实体类字节码对象生成Dao实例化对象
     * @param entityType 实体类字节码对象
     * @param dataResources 数据底层所需数据
     * @param iocHandle IOC容器处理器，预期是需要外层IOC提供实现方法对Dao实现类进行动态代理
     * @param <T> 实体类类型
     * @return Dao实例化对象
     */
    public static <T> IDao<T> getDaoByEntityType(Class<T> entityType , DataResources dataResources , Function<Object,Object> iocHandle, JdbcTransaction jdbcTransaction , YamlTransaction yamlTransaction){
        return generate(entityType, dataResources,iocHandle,jdbcTransaction,yamlTransaction);
    }

    /**
     * 根据实体类字节码对象生成Dao实例化对象
     * @param entityType 实体类字节码对象
     * @param dataResources 数据底层所需数据
     * @param <T> 实体类类型
     * @return Dao实例化对象
     */
    public static <T> IDao<T> getDaoByEntityType(Class<T> entityType , DataResources dataResources , JdbcTransaction jdbcTransaction , YamlTransaction yamlTransaction){
        return getDaoByEntityType(entityType,dataResources,item -> item,jdbcTransaction,yamlTransaction);
    }
}
